/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package model.utility;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.util.ArrayList;

/**
 * Helps with loading plugins
 */
public class PluginLoader {

  /**
   * Load all plugins from directory implementing/extending base
   * @param directory Directory of plugins
   * @param base base class to be extended from
   * @param constructor_parameters parameters of its constructor
   * @return a list of all classes in this plugin
   */
  public static ArrayList<Class> loadPlugins(String directory, Class base,
          Class[] constructor_parameters, String basepackage) {
    ArrayList<Class> ret = new ArrayList<Class>();
    File f = new File(directory);
    if (f == null) {
      throw new Error("Directory cannot be found");
    }
    String names[] = f.list();
    for (int i = 0; i < names.length; ++i) {
      if (!names[i].
              substring(Math.max(0, names[i].length() - 5)).equals(".java")) {
        // file not java
        continue;
      }
      try {
        String classname = names[i].substring(0, names[i].length() - 5);
        classname = basepackage + "." + classname;
        Class newclass = Class.forName(classname);
        int modifier = newclass.getModifiers();
        if (base.isAssignableFrom(newclass)
                && !Modifier.isAbstract(modifier)
                && !Modifier.isInterface(modifier)
                && !Modifier.isStatic(modifier)) {
          // this can be instantiated and implements base
          // check if constructor is found
          newclass.getConstructor(constructor_parameters);
          ret.add(newclass);
        }
      } catch (ClassNotFoundException cex) {
        // class not found, do nothing
      } catch (SecurityException se) {
        // security exception, information not allowed
      } catch (NoSuchMethodException ne) {
        // no matching constructor found
      }
    }
    return ret;

  }

  public static ArrayList<Constructor> loadConstructors(
          String directory, Class base, Class[] constructor_parameters,
          String basepackage) {
    ArrayList<Class> plugins = PluginLoader.loadPlugins(directory, base,
            constructor_parameters, basepackage);
    ArrayList<Constructor> ret = new ArrayList<Constructor>();
    for (Class c : plugins) {
      try {
        ret.add(c.getConstructor(constructor_parameters));
      } catch (NoSuchMethodException nox){
        assert false;
      }
    }
    return ret;
  }
  
  public static ArrayList<Object> loadAndInstantiate(
          String directory, Class base, Class[] constructor_parameters,
          String basepackage, Object[] constructor_real_params) {
    ArrayList<Object> ret = new ArrayList<Object>();
    ArrayList<Constructor> constructors = PluginLoader.loadConstructors(
            directory, base, constructor_parameters, basepackage);
    for (Constructor c : constructors) {
      try {
        ret.add(c.newInstance(constructor_real_params));
      } catch (InstantiationException ie) {
        assert false;
      } catch (IllegalAccessException ia) {
        assert false;
      } catch (InvocationTargetException ite) {
        assert false;
      }
    }
    return ret;
  }

}
